﻿using System;
using System.Text;
using scheduler.Model;

namespace scheduler
{
    public class TimingFuncs
    {
        public static EveryInfo ParseEveryInfo(string sEvery)
        {
            var info = new EveryInfo();

            var parts = sEvery.Split('@');
            var everyPart = parts[0];

            ParseTimeSpan(everyPart, info);

            //
            if (parts.Length > 1)
            {
                info.At = ParseAtInfo(parts[1]);
            }
            else
            {
                info.At = new AtInfo();
            }

            return info;
        }

        private static void ParseTimeSpan(string str, TimeSpanInfo info)
        {
            var sb = new StringBuilder();
            for (var n = 0; n < str.Length; n++)
            {
                var c = str[n];
                if (c == 'd')
                {
                    info.Day = ParseIntAndReset(sb);
                }
                else if (c == 'w')
                {
                    info.Week = ParseIntAndReset(sb);
                }
                else if (c == 'M')
                {
                    info.Month = ParseIntAndReset(sb);
                }
                else if (c == 'h')
                {
                    info.Hour = ParseIntAndReset(sb);
                }
                else if (c == 'm')
                {
                    info.Minute = ParseIntAndReset(sb);
                }
                else if (c == 's')
                {
                    info.Second = ParseIntAndReset(sb);
                }
                else if (c >= '0' && c <= '9')
                {
                    sb.Append(c);
                }
                else
                {
                    sb.Length = 0;
                    Console.WriteLine("bad time span unit " + c);
                }
            }
        }

        private static int ParseIntAndReset(StringBuilder sb)
        {
            var value = int.Parse(sb.ToString());
            sb.Length = 0;
            return value;
        }

        public static AtInfo ParseAtInfo(string sAt)
        {
            var info = new AtInfo();

            if (string.IsNullOrEmpty(sAt))
            {
                return info;
            }
            var parts = sAt.Split(':');
            if (parts[0].Length > 0)
            {
                info.Hour = int.Parse(parts[0]);
                info.Minute = info.Second = 0;
            }
            if (parts.Length > 1)
            {
                if (parts[1].Length > 0)
                {
                    info.Minute = int.Parse(parts[1]);
                    info.Second = 0;
                }
                if (parts.Length > 2)
                {
                    if (parts[2].Length > 0)
                    {
                        info.Second = int.Parse(parts[2]);
                    }
                }
            }

            return info;
        }

        public static DateTime GetNextRepetition(EveryInfo everyInfo, DateTime lastTime)
        {
            var time = new DateTime();

            DateTime relativeTime;
            if (lastTime.Ticks == 0)
            {
                relativeTime = DateTime.Now;
            }
            else
            {
                relativeTime = Offset(lastTime, everyInfo, 1);
            }
            var year = relativeTime.Year;
            var month = relativeTime.Month;
            var day = relativeTime.Day;
            var hour = everyInfo.At.Hour ?? relativeTime.Hour;
            var minute = everyInfo.At.Minute ?? relativeTime.Minute;
            var second = everyInfo.At.Second ?? relativeTime.Second;

            time = new DateTime(year, month, day, hour, minute, second);

            return time;
        }

        private static DateTime Offset(DateTime dateTime, TimeSpanInfo tsi, int dir)
        {
            var result = dateTime.AddMonths(tsi.Month*dir);

            var days = tsi.Week*7 + tsi.Day;
            var minutes = days*(24*60) + tsi.Hour * 60 + tsi.Minute;

            result = result.AddMinutes(minutes*dir);
            result = result.AddSeconds(tsi.Second);
            return result;
        }

        public static DateTime Before(DateTime dueTime, string sBefore)
        {
            var tsi = new TimeSpanInfo();
            ParseTimeSpan(sBefore, tsi);

            var time = Offset(dueTime, tsi, -1);
            return time;
        }
    }
}